package com.dajie.pt.dao.base;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.util.Assert;

import com.dajie.pt.entity.base.BaseEntity;
import com.dajie.pt.entity.base.EntityDao;


/**
 * 
 * @author panbing.wu
 * 
 * @param <E>
 * @param <PK>
 */
public abstract class BaseIbatisDao<E extends BaseEntity, PK extends Serializable> extends SqlMapClientDaoSupport
		implements EntityDao<E, PK>
{
	protected final Log log = LogFactory.getLog(getClass());

	protected abstract Class getEntityClass();

	private static final Long ROWNO = 5000L;

	public SqlMapClientTemplate db()
	{
		return getSqlMapClientTemplate();
	}

	public E getById(final PK primaryKey)
	{
		return (E) getSqlMapClientTemplate().queryForObject(getFindByPrimaryKeyQuery(), primaryKey);
	}

	public E getByIdMap(Map idMap)
	{
		return (E) getSqlMapClientTemplate().queryForObject(getFindByPrimaryKeyMapQuery(), idMap);
	}

	public E getByIdandPID(final PK id, long pid)
	{
		final Object object = getSqlMapClientTemplate().queryForObject(getFindByPrimaryKeyMapQuery(),
				map("id", id, "projectId", pid));
		return (E) object;
	}

	public void deleteById(final PK id)
	{
		deleteBy("id", id);
	}

	public void delete(Collection<PK> ids)
	{
		deleteBy("ids", StringUtils.join(ids, ","));
	}

	public void deleteBy(final Object... params)
	{
		if (checkDeleteParams(params))
		{
			prepareObjectForDelete(null, map(params));
			db().delete(getDeleteQuery(), map(params));
		} else
		{
			log.info("delete params can not be null!");
		}
	}

	public void deleteByWithLog(Object logInfo, final Object... params)
	{
		if (checkDeleteParams(params))
		{
			prepareObjectForDeleteForLog(logInfo, params);
			db().delete(getDeleteQuery(), map(params));
		} else
		{
			log.info("delete params can not be null!");
		}
	}

	public Object save(final E entity)
	{
		prepareObjectForSaveOrUpdate(entity);
		return db().insert(getInsertQuery(), entity);
	}


	public Object replace(final E entity)
	{
		prepareObjectForSaveOrUpdate(entity);
		return  (db().insert(this.getReplaceQuery(), entity));
	}
	
	
	public int update(final E entity)
	{
		prepareObjectForSaveOrUpdate(entity);
		return db().update(getUpdateQuery(), entity);
	}

	public void saveOrUpdate(final E entity)
	{
		if (entity.getId() == null)
		{
			save(entity);
		} else
		{
			update(entity);
		}
	}

	public Object saveWithLog(Object logInfo, final E entity)
	{
		prepareObjectForSaveOrUpdateForLog(logInfo, entity);
		return db().insert(getInsertQuery(), entity);
	}

	public int updateWithLog(Object logInfo, final E entity)
	{
		prepareObjectForSaveOrUpdateForLog(logInfo, entity);
		return db().update(getUpdateQuery(), entity);
	}

	public void saveOrUpdateWithLog(Object logInfo, final E entity)
	{
		if (entity.getId() == null)
		{
			saveWithLog(logInfo, entity);
		} else
		{
			updateWithLog(logInfo, entity);
		}
	}

	/**
	 * 用于子类覆盖,在insert,update之前调用.
	 */
	protected void prepareObjectForSaveOrUpdate(final E o)
	{
	}

	protected void prepareObjectForSaveOrUpdateForLog(Object logInfo, final E o)
	{
	}

	protected void prepareObjectForDelete(final Object... map)
	{
	}

	protected void prepareObjectForDeleteForLog(Object logInfo, final Object... map)
	{
	}

	public String getFindByPrimaryKeyQuery()
	{
		return getEntityClass().getSimpleName() + ".getById";
	}

	public String getFindByPrimaryKeyMapQuery()
	{
		return getEntityClass().getSimpleName() + ".getByIdWithMap";
	}

	public String getInsertQuery()
	{
		return getInsertQuery(getEntityClass());
	}

	private String getInsertQuery(final Class clazz)
	{
		return clazz.getSimpleName() + ".insert";
	}

	public String getUpdateQuery()
	{
		return getUpdateQuery(getEntityClass());
	}

	public String getReplaceQuery()
	{
		return getReplaceQuery(getEntityClass());
	}
	
	private String getReplaceQuery(final Class clazz)
	{
		return clazz.getSimpleName() + ".replace";
	}
	
	private String getUpdateQuery(final Class clazz)
	{
		return clazz.getSimpleName() + ".update";
	}

	public String getDeleteQuery()
	{
		return getDeleteQuery(getEntityClass());
	}

	private String getDeleteQuery(final Class clazz)
	{
		return clazz.getSimpleName() + ".delete";
	}

	public String getCountQuery()
	{
		return getEntityClass().getSimpleName() + ".count";
	}

	public String getPageSelect()
	{
		return getEntityClass().getSimpleName() + ".pageSelect";
	}

	public int count(final Object... params)
	{
		final Map map = map(params);
		final Number count = (Number) this.getSqlMapClientTemplate().queryForObject(getCountQuery(), map);
		return count.intValue();
	}



	public List<E> findByMap(final Map map)
	{
		return getSqlMapClientTemplate().queryForList(getPageSelect(), map);
	}

	public List<E> findBy(final Object... params)
	{
		return getSqlMapClientTemplate().queryForList(getPageSelect(), map(params));
	}

	public E findUniqueBy(final Object... params)
	{
		Map<String,String> map = map(params);
		map(params).put("sortColumns ","id desc");
		List<E> list = this.findByMap(map);
		if(list != null && list.size() > 0)
		{
			return (E)list.get(0);
		}else
		{
			return null;
		}
		//return (E) getSqlMapClientTemplate().queryForObject(getPageSelect(), map(params));
	}

	public boolean isPropertyUnique(final String property, final String orgValue, final String newValue)
	{
		return newValue.equals(orgValue) || (findUniqueBy(property, newValue) == null);
	}

	public List findAll()
	{
		return getSqlMapClientTemplate().queryForList(getPageSelect(), map());
	}

	public boolean isUnique(final E entity, final String uniquePropertyNames)
	{
		throw new UnsupportedOperationException();
	}

	public void flush()
	{
		// DO NOTHING
	}

	/**
	 * 根据参数构造map，参数必须为偶数个，依次为key1，value1，key2，value2…….
	 * 
	 * @param datas
	 *            参数列表
	 * @return 构造出的map
	 */
	protected Map map(final Object... datas)
	{
		Assert.isTrue(datas.length % 2 == 0, "参数必须为偶数个");
		final Map map = new HashMap();
		for (int i = 0; i < datas.length; i += 2)
		{
			map.put(datas[i], datas[i + 1]);
		}
		return map;
	}

	protected boolean checkDeleteParams(final Object... params)
	{
		for (Object param : params)
		{
			if (param == null || StringUtils.isEmpty(param.toString()))
			{
				log.info("删除参数不能为空");
				return false;
			}
			log.info("deleteParam:" + param.toString());
		}
		return true;
	}

	public List<String> getAllByPage(String sql, Map map)
	{
		List<String> listAll = new ArrayList<String>();
		List<String> list = null;
		int fromRow = 0;
		do
		{
			map.put("fromRow", fromRow);
			map.put("toRow", ROWNO);
			list = db().queryForList(sql, map);
			logger.info("getAllByPage:sql(" + sql + "),list.size(" + list.size() + "),rows(" + fromRow + ")");
			fromRow += ROWNO;
			listAll.addAll(list);
		} while (list != null && list.size() != 0 && list.size() == ROWNO);

		return listAll;
	}
}
